package com.wang.tool.valve.strategy;

import com.wang.tool.util.CommonUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @Description 数字尾部范围模型
 * @Author shaopeng
 * @Date 2021/11/21
 * @Version 1.0
 */
public class RangeValve extends AbsValve {
    private static final long serialVersionUID = 2052378943488856511L;

    /**
     * 策略名
     */
    public static final String STRATEGY = "range";

    /**
     * 解析后的配置
     * null: 配置异常/还未初始化
     * 空集合: 全量通过
     */
    private transient List<Range> _ranges;

    /**
     * 类似于  ["3-026","1-9"]
     * 表示 3<=最后三位数字<=26 || 1<=最后1位数字<=9
     * <p>
     * 为什么上限数字支持前面用0占位?
     * 例如  3-026
     * 如果我想表示截取后面三位数字 在3-26范围  其实我们想要的结果是 0-999中选取这 24个数字  总占比是 2.4%
     * 常规的 3-26表示,程序拿到这个配置没法判断他是要截取几位进行判断,只能想当然的取后2位,这时场景就变成了 0-99中选取那24个数字 总占比是24%
     */
    private List<String> ranges;


    @Override
    public void prepare() {
        if (CommonUtil.isEmpty(ranges)) {
            _ranges = new ArrayList<>();
            return;
        }
        List<Range> tmpRanges = new ArrayList<>();
        for (String range : ranges) {
            if (CommonUtil.isBlank(range)) {
                continue;
            }
            String[] split = range.split("-");
            if (split.length != 2) {
                throw new IllegalArgumentException("range must like  min-max");
            }

            String min = split[0];
            String max = split[1];

            if (!CommonUtil.isNumeric(max)) {
                throw new IllegalArgumentException("max must be numberStr");
            }
            if (!CommonUtil.isNumeric(min)) {
                throw new IllegalArgumentException("min must be numberStr");
            }

            Long minNum = Long.parseLong(min);
            Long maxNum = Long.parseLong(max);
            Integer tailLength = max.length();
            tmpRanges.add(new Range(tailLength, minNum, maxNum));
        }
        _ranges = tmpRanges;
    }

    @Override
    protected boolean internalIsPass(Object param) {
        if (_ranges == null) {
            return false;
        }
        if (_ranges.isEmpty()) {
            return true;
        }
        if (param == null || !CommonUtil.isNumeric(param.toString())) {
            return false;
        }
        String str = param.toString();
        for (Range range : _ranges) {
            long num = Long.parseLong(str.substring(Math.max(0, str.length() - range.getTailLength())));
            if (range.getMinNum() <= num && num <= range.getMaxNum()) {
                return true;
            }
        }
        return false;
    }

    public List<String> getRanges() {
        return ranges;
    }

    public void setRanges(List<String> ranges) {
        this.ranges = ranges;
    }

    @Override
    public String toString() {
        return "{strategy:" + STRATEGY +
                ", ranges:" + _ranges +
                '}';
    }

    public class Range {

        /**
         * 比对长度
         */
        private int tailLength;

        /**
         * 最小值 包含
         */
        private long minNum;

        /**
         * 最大值 包含
         */
        private long maxNum;


        public Range(int tailLength, long minNum, long maxNum) {
            this.tailLength = tailLength;
            this.maxNum = maxNum;
            this.minNum = minNum;
        }

        public int getTailLength() {
            return tailLength;
        }

        public void setTailLength(int tailLength) {
            this.tailLength = tailLength;
        }

        public long getMinNum() {
            return minNum;
        }

        public void setMinNum(long minNum) {
            this.minNum = minNum;
        }

        public long getMaxNum() {
            return maxNum;
        }

        public void setMaxNum(long maxNum) {
            this.maxNum = maxNum;
        }

        @Override
        public String toString() {
            return "(" + tailLength + "," + minNum + "," + maxNum + ")";
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()){
                return false;
            }
            Range range = (Range) o;
            return Objects.equals(tailLength, range.tailLength) &&
                    Objects.equals(minNum, range.minNum) &&
                    Objects.equals(maxNum, range.maxNum);
        }

        @Override
        public int hashCode() {
            return Objects.hash(tailLength, minNum, maxNum);
        }
    }
}
